package com.cheney.demo.util;

import cn.hutool.core.codec.Base64;
import cn.hutool.core.util.ObjectUtil;
import cn.hutool.core.util.StrUtil;
import cn.hutool.crypto.KeyUtil;
import cn.hutool.crypto.SecureUtil;
import com.alibaba.fastjson.JSONObject;
import com.cheney.common.core.exception.BaseException;
import com.cheney.demo.config.RedisCache;
import com.cheney.demo.model.WechatAppletLoginDTO;
import com.cheney.demo.model.WechatAppletLoginVO;
import com.cheney.demo.model.WechatSubscribeDTO;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.http.ResponseEntity;
import org.springframework.stereotype.Component;

import java.util.HashMap;
import java.util.Map;
import java.util.concurrent.TimeUnit;

/**
 * 微信小程序工具类
 *
 * @author chensi
 * @date 2021/11/15 11:48
 */
@Component
public class WechatAppletUtil {

    @Autowired
    private RedisCache redisCache;

    @Autowired
    private RestTemplateUtils restTemplateUtils;

    @Value("${wx.applet.appId}")
    private String appId;

    @Value("${wx.applet.appSecret}")
    private String appSecret;

    /**
     * 获取微信accessToken
     */
    public synchronized String getAccessToken() {
        String accessToken = redisCache.getCacheObject("cheney-platform:wx:applet:accessToken");
        if (StrUtil.isBlank(accessToken)) {
            String url = "https://api.weixin.qq.com/cgi-bin/token?grant_type={grant_type}&appid={appid}&secret={secret}";
            Map<String, Object> param = new HashMap<>();
            param.put("grant_type", "client_credential");
            param.put("appid", appId);
            param.put("secret", appSecret);
            try {
                ResponseEntity<String> res = restTemplateUtils.get(url, String.class, param);
                JSONObject jsonObject = JSONObject.parseObject(res.getBody());
                accessToken = jsonObject.get("access_token").toString();
                if (StrUtil.isNotBlank(accessToken)) {
                    redisCache.setCacheObject("cheney-platform:wx:applet:accessToken", accessToken, 7000, TimeUnit.SECONDS);
                } else {
                    throw new BaseException("获取微信accessToken出错");
                }
            } catch (Exception e) {
                e.printStackTrace();
                throw new BaseException("获取微信accessToken出错");
            }
        }
        return accessToken;
    }

    /**
     * 登录
     * 步骤:(个人):
     * 第一步:微信小程序调用login和getUserInfo(),拿到code和encryptedData,iv,传入到后台进行业务处理
     * 第二步:拿到code调用登录凭证校验接口去获取用户,openid和session_key
     * 第三步:获取到session_key和encryptedData,iv去进行AES解密,解密成功后就可以拿到用户信息
     */
    public WechatAppletLoginDTO login(WechatAppletLoginVO vo) {
        String url = "https://api.weixin.qq.com/sns/jscode2session?appid={appid}&secret={secret}&js_code={js_code}&grant_type=authorization_code";
        Map<String, Object> param = new HashMap<>();
        param.put("appid", appId);
        param.put("secret", appSecret);
        param.put("js_code", vo.getJsCode());
        ResponseEntity<String> res = restTemplateUtils.get(url, String.class, param);
        JSONObject jsonObject = JSONObject.parseObject(res.getBody());
        if (ObjectUtil.isNull(jsonObject.get("session_key")) || ObjectUtil.isNull(jsonObject.get("openid"))) {
            throw new BaseException("获取微信session_key失败，请稍后再试~");
        }
        String sessionKey = jsonObject.get("session_key").toString();
        String openId = jsonObject.get("openid").toString();
        String phone;
        try {
            String decryptStr = decryptWx(vo.getEncryptedData(), sessionKey, vo.getIv());
            //open-type="getPhoneNumber" 此处是解密手机号，因为三方登录场景大多是按照openid或手机号来判定
            phone = JSONObject.parseObject(decryptStr).get("phoneNumber").toString();
        } catch (Exception e) {
            e.printStackTrace();
            throw new BaseException("微信解密失败，请稍后再试~");
        }
        WechatAppletLoginDTO wechatAppletLoginDTO = new WechatAppletLoginDTO();
        wechatAppletLoginDTO.setPhone(phone);
        wechatAppletLoginDTO.setOpenId(openId);
        return wechatAppletLoginDTO;
    }

    /**
     * 发送订阅消息
     */
    public String sendSubscribe(WechatSubscribeDTO dto) {
        String url = "https://api.weixin.qq.com/cgi-bin/message/subscribe/send?access_token={access_token}";
        Map<String, Object> param = new HashMap<>();
        param.put("access_token", getAccessToken());
        ResponseEntity<String> res = restTemplateUtils.post(url, dto, String.class, param);
        return res.getBody();
    }


    /**
     * 微信解密（加密字符串、sessionKey、iv==>明文）
     * 对称解密使用的算法为 AES-128-CBC，数据采用PKCS#7填充。
     * 对称解密的目标密文为 Base64_Decode(encryptedData)。
     * 对称解密秘钥 aeskey = Base64_Decode(session_key), aeskey 是16字节。
     * 对称解密算法初始向量 为Base64_Decode(iv)，其中iv由数据接口返回。
     */
    public String decryptWx(String encryptedData, String sessionKey, String iv) {
        return SecureUtil.aes()
                .setIv(Base64.decode(iv))
                .init("AES/CBC/PKCS7Padding", KeyUtil.generateKey("AES", Base64.decode(sessionKey)))
                .decryptStr(Base64.decode(encryptedData));
    }

    /**
     * 身份证识别
     */
    public String ocrIdcard(String type, String imgUrl) {
        String url = "https://api.weixin.qq.com/cv/ocr/idcard?type={type}&img_url={img_url}&access_token={access_token}";
        Map<String, Object> param = new HashMap<>();
        param.put("type", type);
        param.put("img_url", imgUrl);
        param.put("access_token", getAccessToken());
        ResponseEntity<String> res = restTemplateUtils.post(url, null, String.class, param);
        return res.getBody();
    }
}
